import axios from 'axios'
import type {
    AxiosInstance,
    AxiosResponse,
    Canceler,
    InternalAxiosRequestConfig,
    AxiosRequestConfig,
} from 'axios'
import { getToken, redirectLogin, removeToken, messageBox } from '@/utils/tools'
import config from '@/config'

// 请求参数 config 定义
export interface RequestConfig extends AxiosRequestConfig {
    isIntercept?: boolean // 是否推入全局的请求拦截栈, 主要做批量取消请求的操作
}

// 响应参数 定义
export type ResponseData<T, X> = X extends true
    ? T
    : {
          code: number
          data: null | T
          message: string
      }

let isWaitToLogin = false // 已经有异步跳转登录页面标识
const outLogin = () => {
    removeToken()
    isWaitToLogin = true
    setTimeout(() => {
        redirectLogin()
    }, 200)
}

// 实例化请求配置
const instance: AxiosInstance = axios.create({
    baseURL: config.domain,
    timeout: config.requestTimeOut,
    validateStatus: () => true, // 使用async-await，处理reject情况较为繁琐，所以全部返回resolve，在业务代码中处理异常
})

// 请求拦截器
instance.interceptors.request.use(
    (params: InternalAxiosRequestConfig<any>) => {
        if (params && params.headers) {
            params.headers['Cache-Control'] = 'no-cache, no-store'
            params.headers.Pragma = 'no-cache'
            params.headers.authorization = getToken()
        }
        return params
    },
    error => {
        return Promise.reject(error)
    },
)

// 响应拦截器
instance.interceptors.response.use(
    // 请求成功
    (response: AxiosResponse<any>) => {
        if (response.status === 200) {
            const { code, msg } = response.data
            // 1、存在code, 且是获取数据成功的code
            if (code === '200') {
                return response.data
            }
            // 2、存在code, 但是上一步骤没有通过，可以认为是获取数据失败的code
            if (typeof code === 'number' || typeof code === 'string') {
                if (code === '403') {
                    messageBox('登录过期，请重新登录即将跳转登录页...')
                    outLogin()
                } else {
                    messageBox(msg)
                }

                return Promise.reject(response.data)
            }
            // 3、不存在code的数据，无法确认是获取成功， 还是失败，由业务代码自行判断
            return response.data
        } else {
            messageBox(response.data.msg)
        }
    },
    // 请求失败
    error => {
        if (axios.isCancel(error)) {
            console.warn(error.message || '请求失败，请联系管理员')
        }
    }
)

const CancelToken = axios.CancelToken
const interceptList: Canceler[] = []

// R为false时使用标准的返回体数据格式 R为true表示不使用任务封装返回体数据格式
const request = <T, R = false>(option: RequestConfig) => {
    if (option.isIntercept) {
        option.cancelToken = new CancelToken(cancel => {
            interceptList.push(cancel)
        })
    }
    return instance.request<any, ResponseData<T, R>>(option)
}
export default request
